home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
019a
/
tde10src.zip
/
FINDREP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-05
|
28KB
|
943 lines
/******************* start of original comments ********************/
/*
* Written by Douglas Thomson (1989/1990)
*
* This source code is released into the public domain.
*/
/*
* Name: dte - Doug's Text Editor program - find/replace module
* Purpose: This file contains the functions relating to finding text
* and replacing text.
* It also contains the code for moving the cursor to various
* other positions in the file.
* File: findrep.c
* Author: Douglas Thomson
* System: this file is intended to be system-independent
* Date: October 1, 1989
*/
/********************* end of original comments ********************/
/*
* The search and replace routines have been EXTENSIVELY rewritten.
* The "brute force" search algorithm has been replaced by the Boyer-Moore
* text search algorithm. This search should speed up search and replace
* operations. Replace and search prompting has been changed.
*
* I am not very fond of the Wordstar/TURBO type search and replace prompting.
* Once the search pattern has been defined, one only needs to press a key
* to search forwards or backwards. The forward or backward search key may
* be pressed at any time in any file once the pattern has been entered.
*
* New editor name: tde, the Thomson-Davis Editor.
* Author: Frank Davis
* Date: June 5, 1991
*
* This modification of Douglas Thomson's code is released into the
* public domain, Frank Davis. You may distribute it freely.
*/
#include "tdestr.h"
#include "common.h"
#include "tdefunc.h"
/*
* Name: get_replacement_flags
* Purpose: To input find and replace flags.
* Date: June 5, 1991
* Passed: line: prompt line
* Returns: OK if flags were entered, ERROR if user wanted to abort
*/
int get_replacement_flags( line )
int line;
{
char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */
char *find_opt = "Options (P)rompt before replace (N)o prompt: ";
int len, col, flash, normal, c, rc;
len = strlen( find_opt );
col = len;
flash = g_display.message_color;
normal = g_display.text_color;
save_screen_line( 0, line, line_buff );
s_output( find_opt, line, 0, flash );
eol_clear( col, line, normal );
col += 2;
xygoto( col, line );
c = (c=getch()) != 0 ? c : getch() | 0x100;
while (c != 'P' && c != 'p' && c != 'N' && c != 'n' &&
c != ESC && c != RTURN)
c = (c=getch()) != 0 ? c : getch() | 0x100;
restore_screen_line( 0, line, line_buff );
switch (c) {
case 'P' :
case 'p' :
case RTURN :
g_status.replace_flag = PROMPT;
rc = OK;
break;
case 'N' :
case 'n' :
g_status.replace_flag = NOPROMPT;
rc = OK;
break;
default :
rc = ERROR;
}
bm.search_defined = rc;
return( rc );
}
/*
* Name: ask_replace
* Purpose: Ask user if he want to (r)place, (s)kip, or (e)xit.
* Date: June 5, 1991
* Returns: TRUE if user wants to replace, ERROR otherwise.
* Passed: window: information allowing access to current window etc
* finished: TRUE if user pressed ESC or (e)xit.
*/
int ask_replace( window, finished )
windows *window;
int *finished;
{
char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */
char *ask_opt = "(R)eplace (S)kip (E)xit";
int col, flash, normal, c, rc, prompt_line;
prompt_line = window->cline - 1;
col = 39 - (strlen( ask_opt ) >> 1);
flash = g_display.message_color;
normal = g_display.text_color;
save_screen_line( 0, prompt_line, line_buff );
s_output( ask_opt, prompt_line, col, flash );
c = (c=getch()) != 0 ? c : getch() | 0x100;
while (c != 'R' && c != 'r' && c != 'S' && c != 's' &&
c != 'E' && c != 'e' && c != ESC)
c = (c=getch()) != 0 ? c : getch() | 0x100;
restore_screen_line( 0, prompt_line, line_buff );
switch (c) {
case 'R' :
case 'r' :
rc = OK;
break;
case 'E' :
case 'e' :
case ESC :
*finished = TRUE;
case 'S' :
case 's' :
rc = ERROR;
break;
}
return( rc );
}
/*
* Name: do_replace
* Purpose: To replace text once match has been found.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
* start: location of start of matched text
*/
void do_replace( window, start, direction )
windows *window;
text_ptr start;
int direction;
{
int old_len; /* length of original text */
int new_len; /* length of replacement text */
int net_change;
text_ptr source; /* source of block move */
text_ptr dest; /* destination of block move */
long number; /* number of characters moved */
windows *above; /* window above current */
windows *below; /* window below current */
old_len = strlen( g_status.pattern );
new_len = strlen( g_status.subst );
/*
* move the text to either make room for the extra replacement text
* or to close up the gap left
*/
source = start + old_len;
dest = start + new_len;
number = ptoul( g_status.end_mem ) - ptoul( source );
hw_move( dest, source, number );
/*
* insert the replacement text
*/
for (dest=start, source=g_status.subst; *source;)
*dest++ = *source++;
net_change = new_len - old_len;
adjust_start_end( window->file_info, net_change );
if (direction == FORWARD)
window->rcol += net_change;
adjust_windows_cursor( window, (long)net_change, 0 );
g_status.end_mem = addltop( (long)net_change, g_status.end_mem );
show_avail_mem( );
above = below = window;
while (above->prev || below->next) {
if (above->prev) {
above = above->prev;
if (window->file_info == above->file_info)
if (window->rline < above->rline)
above->cursor = addltop( (long)net_change, above->cursor );
} else if (below->next) {
below = below->next;
if (window->file_info == below->file_info)
if (window->rline < below->rline)
below->cursor = addltop( (long)net_change, below->cursor );
}
}
}
/*
* Name: find_string
* Purpose: To set up and perform a find operation.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
* direction: find FORWARD or BACKWARD - determined by pressed key
* new_string: search for new string to be entered by user
* Notes: If user presses search string key then keep boyer-moore stuff.
* To find the next occurence either forwards or backwards all the
* user has to do is press the "RepeatFindForward" or
* "RepeatFindBackward" key at any time.
*/
void find_string( window, direction, new_string )
windows *window;
int direction;
int new_string;
{
char pattern[MAX_COLS]; /* text to be found */
int rcol, prompt_line;
long pattern_line, rline, test_line;
text_ptr pattern_location;
text_ptr p, q;
char *snf = "string not found";
prompt_line = window->bottom_line;
/*
* get search text, using previous as default
*/
if (new_string == TRUE) {
strcpy( pattern, bm.pattern );
if (get_name( "String to find: ", prompt_line, pattern,
g_display.message_color ) != OK)
return;
bm.search_defined = OK;
strcpy( bm.pattern, pattern );
build_boyer_array( );
}
if (bm.search_defined == OK) {
if (direction == FORWARD) {
if ((pattern_location = forward_boyer_moore_search( window )) != NULL)
find_adjust( window, pattern_location );
else
error( WARNING, prompt_line, snf );
} else {
if ((pattern_location = backward_boyer_moore_search( window )) != NULL)
find_adjust( window, pattern_location );
else
error( WARNING, prompt_line, snf );
}
} else
error( WARNING, prompt_line, "find pattern not defined" );
}
/*
* Name: build_boyer_array
* Purpose: To set up the boyer array for foward and backward searches.
* Date: June 5, 1991
* Notes: Set up one array for forward searches and another for backward
* searches. If user decides to ignore case then fill in array
* with reverse case characters.
*/
void build_boyer_array( )
{
int i, pl;
char *p;
/*
* set up for forward search
*/
i = pl = bm.pattern_length = strlen( bm.pattern );
/*
* set skip index of all characters to length of string
*/
memset( bm.skip_forward, i, 256 );
i--;
/*
* for each character in string, set the skip index
*/
for (p=bm.pattern; i>=0; i--, p++)
bm.skip_forward[*p] = i;
if (bm.search_case == IGNORE) {
i = pl - 1;
for (p=bm.pattern; i>=0; i--, p++) {
if (*p >= 'A' && *p <= 'Z')
bm.skip_forward[*p+32] = i;
else if (*p >= 'a' && *p <= 'z')
bm.skip_forward[*p-32] = i;
}
}
/*
* set up for backward search
*/
i = -pl;
memset( bm.skip_backward, i, 256 );
i++;
for (p=bm.pattern+pl-1; i<=0; i++, p--)
bm.skip_backward[*p] = i;
if (bm.search_case == IGNORE) {
i = -pl + 1;
for (p=bm.pattern+pl-1; i<=0; i++, p--) {
if (*p >= 'A' && *p <= 'Z')
bm.skip_backward[*p+32] = i;
else if (*p >= 'a' && *p <= 'z')
bm.skip_backward[*p-32] = i;
}
}
}
/*
* Name: foward_boyer_moore_search
* Purpose: search forward for pattern using boyer array
* Passed: window: information allowing access to current window etc
* Returns: position in file if found else return NULL
* Date: June 5, 1991
* Notes: Start searching from cursor position to end of file. If we hit
* end of file without a match, start searching from the beginning
* of file to cursor position. (do wrapped searches)
*/
text_ptr forward_boyer_moore_search( window )
windows *window;
{
int i, pl;
text_ptr p, q, s, search_start;
long search_length;
/*
* if cursor is beyond end of line then start at end of line
*/
window->cursor = cpf( window->cursor );
search_start = window->cursor;
i = window->rcol + 1;
pl = linelen( window->cursor );
if (i > pl)
i = pl;
/*
* make start of search 1 character greater than current position so
* we don't repeatedly find the pattern at current position.
*/
search_start += i;
s = search_start;
pl = bm.pattern_length;
i = pl - 1;
/*
* find out how many character to search. do standard Boyer-Moore search
*/
search_length = ptoul( window->file_info->end_text ) - ptoul( s );
p = bm.pattern;
for (search_length -= i; search_length >= 0; search_length -= i) {
s = s + i;
i = bm.skip_forward[*s];
if (i == 0) {
q = s + 1 - pl;
q = cpf( q );
if (bm.search_case == MATCH)
i = strncmp( q, p, pl );
else
i = strnicmp( q, p, pl );
if (i == 0)
return( q );
i = 1;
}
s = cpf( s );
}
/*
* haven't found pattern yet - now search from beginning of file
*/
s = cpf( window->file_info->start_text );
search_length = ptoul( search_start ) + 1 - ptoul( s );
i = pl - 1;
for (search_length -= i; search_length >= 0; search_length -= i) {
s = s + i;
i = bm.skip_forward[*s];
if (i == 0) {
q = s + 1 - pl;
q = cpf( q );
if (bm.search_case == MATCH)
i = strncmp( q, p, pl );
else
i = strnicmp( q, p, pl );
if (i == 0)
return( q );
i = 1;
}
s = cpf( s );
}
/*
* pattern not found - return NULL pointer
*/
return( NULL );
}
/*
* Name: backward_boyer_moore_search
* Purpose: search backward for pattern using boyer array
* Passed: window: information allowing access to current window etc
* Returns: position in file if found else return NULL
* Date: June 5, 1991
* Notes: Start searching from cursor position to beginning of file. If we
* hit beginning end of file without a match, start searching from the
* end of file to cursor position. (do wrapped searches)
*/
text_ptr backward_boyer_moore_search( window )
windows *window;
{
int i, pl;
text_ptr p, s, search_start;
long search_length;
window->cursor = cpb( window->cursor );
search_start = window->cursor;
i = window->rcol - 1;
pl = linelen( window->cursor );
if (i > pl)
i = pl;
search_start += i;
s = search_start;
pl = bm.pattern_length;
i = -pl + 1;
search_length = ptoul( search_start ) + 1 -
ptoul( window->file_info->start_text );
p = bm.pattern;
for (search_length += i; search_length >= 0; search_length += i) {
s = s + i;
i = bm.skip_backward[*s];
if (i == 0) {
if (bm.search_case == MATCH)
i = strncmp( s, p, pl );
else
i = strnicmp( s, p, pl );
if (i == 0)
return( s );
i = -1;
}
s = cpb( s );
}
/*
* haven't found pattern yet - now search from end of file
*/
window->file_info->end_text = cpb( window->file_info->end_text );
s = window->file_info->end_text - 1;
i = -pl + 1;
search_length = ptoul( s ) + 1 - ptoul( search_start );
for (search_length += i; search_length >= 0; search_length += i) {
s = s + i;
i = bm.skip_backward[*s];
if (i == 0) {
if (bm.search_case == MATCH)
i = strncmp( s, p, pl );
else
i = strnicmp( s, p, pl );
if (i == 0)
return( s );
i = -1;
}
s = cpb( s );
}
/*
* pattern not found - return NULL pointer
*/
return( NULL );
}
/*
* Name: find_adjust
* Purpose: place cursor on screen given a posiont in file - default cline
* Passed: window: information allowing access to current window etc
* found: position anywhere in file
* Date: June 5, 1991
* Notes: found could be anywhere in file. Find the start of line that
* found is on. Determine if start of line is behind or ahead of
* current line. Once that is done, it is easy to determine if found
* is on screen. Lastly, current cursor position becomes start of
* found line - reposition and display.
*/
void find_adjust( window, found )
windows *window;
text_ptr found;
{
int rcol;
long pattern_line, rline, test_line;
text_ptr p, q;
/*
* find start of line found is on.
*/
found = cpb( found );
if (*(found - 1) != '\n' && *(found - 1) != CONTROL_Z)
p = find_prev( found );
else
p = found;
/*
* find real column found is on.
*/
rcol = ptoul( found ) - ptoul( p );
rline = pattern_line = window->rline;
q = window->cursor = cpf( window->cursor );
/*
* if p, start of found line, is greater than current line, see if
* p is between current line and bottom line on screen.
*/
if (ptoul( q ) < ptoul( p )) {
while (ptoul( q ) != ptoul( p )) {
q = find_next( q );
++pattern_line;
}
test_line = pattern_line - rline;
if ((long)window->cline + test_line <= (long)window->bottom_line)
window->cline += test_line;
else
window->dirty = LOCAL;
/*
* if p, start of found line, is less than current line, see if
* p is between current line and top line on screen.
*/
} else if (ptoul( q ) > ptoul( p )) {
q = cpb( q );
while (ptoul( q ) != ptoul( p )) {
q = find_prev( q );
--pattern_line;
}
test_line = rline - pattern_line;
if ((long)window->cline - test_line > (long)(window->top_line-1))
window->cline -= test_line;
else
window->dirty = LOCAL;
if (pattern_line < (long)(window->cline - (window->top_line-1)))
window->cline = pattern_line + window->top_line - 1;
}
/*
* cursor line becomes found line. check if column is on screen.
*/
window->cursor = p;
window->rline = pattern_line;
check_virtual_col( window, rcol, rcol );
}
/*
* Name: replace_string
* Purpose: To set up and perform a replace operation.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
* direction: replace forward or backward in file
*/
void replace_string( window, direction )
windows *window;
int direction;
{
char pattern[MAX_COLS]; /* the old and replacement text */
int prompt_line;
int net_change;
int sub_len;
int file_changed;
int finished;
int rc;
text_ptr pattern_location;
text_ptr replace_start;
unsigned long rs, pl;
windows wp;
int color;
char *snf = "string not found";
prompt_line = window->bottom_line;
color = g_display.message_color;
/*
* get the old text, using the previous as the default
*/
strcpy( pattern, g_status.pattern );
if (get_name( "String to find: ", prompt_line, pattern, color ) != OK)
return;
strcpy( g_status.pattern, pattern );
/*
* get the replacement text, using the previous as the default
*/
strcpy( pattern, g_status.subst );
if (get_name( "Replacement: ", prompt_line, pattern, color ) != OK)
return;
strcpy( g_status.subst, pattern );
sub_len = strlen( pattern );
strcpy( bm.pattern, g_status.pattern );
net_change = sub_len - strlen( g_status.pattern );
/*
* get the replace flags
*/
if (get_replacement_flags( prompt_line ) != OK)
return;
build_boyer_array( );
dup_window_info( &wp, window );
finished = FALSE;
file_changed = FALSE;
if (direction == FORWARD) {
if ((replace_start = forward_boyer_moore_search( &wp )) != NULL) {
rs = ptoul( replace_start );
replace_and_display( &wp, replace_start, &finished, &file_changed,
direction );
} else {
error( WARNING, prompt_line, snf );
finished = TRUE;
}
while (finished == FALSE) {
if ((pattern_location = forward_boyer_moore_search( &wp )) != NULL) {
pl = ptoul( pattern_location );
if (rs == pl)
finished = TRUE;
else {
rc = replace_and_display( &wp, pattern_location, &finished,
&file_changed, direction );
if (rc == OK && rs > pl)
rs += net_change;
}
} else {
error( WARNING, prompt_line, snf );
finished = TRUE;
}
}
} else {
if ((replace_start = backward_boyer_moore_search( &wp )) != NULL) {
rs = ptoul( replace_start );
replace_and_display( &wp, replace_start, &finished, &file_changed,
direction );
} else {
error( WARNING, prompt_line, snf );
finished = TRUE;
}
while (finished == FALSE) {
if ((pattern_location = backward_boyer_moore_search( &wp )) != NULL) {
pl = ptoul( pattern_location );
if (rs == pl || (pl > rs && rs > pl - sub_len))
finished = TRUE;
else {
rc = replace_and_display( &wp, pattern_location, &finished,
&file_changed, direction );
if (rc == OK && rs > pl)
rs += net_change;
}
} else {
finished = TRUE;
error( WARNING, prompt_line, snf );
}
}
}
dup_window_info( window, &wp );
if (file_changed)
window->file_info->modified = TRUE;
}
/*
* Name: replace_and_display
* Purpose: To make room for replacement string
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
* pattern_location: pointer to position of pattern in file
* finished: boolean - stop replacing on this occurrence
* modified: boolean - user may decide to skip this replacement
* Notes: Show user where pattern_location is on screen if needed.
* Replace and display if needed.
*/
int replace_and_display( window, pattern_location, finished, modified,
direction )
windows *window;
text_ptr pattern_location;
int *finished, *modified;
int direction;
{
int rc, ask_user;
ask_user = g_status.replace_flag;
find_adjust( window, pattern_location );
xygoto( window->ccol, window->cline );
if (window->dirty) {
display_current_window( window );
window->dirty = FALSE;
}
rc = OK;
if (ask_user == PROMPT) {
show_line_col( window );
rc = ask_replace( window, finished );
}
if (rc == OK) {
do_replace( window, pattern_location, direction );
*modified = TRUE;
window->dirty = GLOBAL;
if (ask_user == PROMPT)
show_changed_line( window );
}
return( rc );
}
/*
* Name: goto_top_file
* Purpose: To move the cursor to the top of the file.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
*/
void goto_top_file( window )
windows *window;
{
text_ptr next; /* successive lines above the cursor */
int i;
if (window->rline != window->cline - (window->top_line-1)) {
next = cpf( window->file_info->start_text );
for (i=window->cline; i>window->top_line; i--)
next = find_next( next );
window->cursor = next;
window->rline = window->cline - (window->top_line-1);
display_current_window( window );
}
}
/*
* Name: goto_end_file
* Purpose: To move the cursor to the end of the file.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
*/
void goto_end_file( window )
windows *window;
{
text_ptr prev;
int i;
int vir_row;
prev = cpb( window->file_info->end_text ) - 1;
for (i=window->page; i>=0 && prev != NULL; i--)
prev = find_prev( prev );
if (prev != NULL && ptoul( prev ) > ptoul( window->cursor )) {
vir_row = window->bottom_line - window->cline;
window->rline = window->file_info->length - vir_row;
prev = cpf( prev );
for (i=window->top_line; i<window->cline; i++)
prev = find_next( prev );
window->cursor = prev;
display_current_window( window );
}
}
/*
* Name: scan_forward
* Purpose: To find the corresponding occurrence of target, ignoring
* embedded pairs of opp and target, searching forwards.
* Date: June 5, 1991
* Passed: start: position of character to be paired
* opp: the opposite to target, if any
* target: the string to be found
* Returns: the location of the corresponding target in the text buffer
* Notes: Every 8,000 characters, check pointer forward.
*/
text_ptr scan_forward( start, opp, target )
text_ptr start;
char opp;
char target;
{
text_ptr orig;
int count = 0; /* number of unmatched opposites found */
int check = 0;
char c;
orig = start = cpf( start );
while ((c = *++start) && (c != CONTROL_Z)) {
check++;
if (opp == c)
count++;
else if (target == c) {
if (count == 0)
break;
--count;
}
if (check > 8000) {
start = cpf( start );
check = 0;
}
}
if (c == CONTROL_Z)
start = orig;
return( start );
}
/*
* Name: scan_backward
* Purpose: To find the corresponding occurrence of target, ignoring
* embedded pairs of opp and target, searching backwards.
* Date: June 5, 1991
* Passed: start: position of character to be paired
* opp: the opposite to target, if any
* target: the string to be found
* Returns: the location of the corresponding target in the text buffer
*/
text_ptr scan_backward( start, opp, target )
text_ptr start;
char opp;
char target;
{
text_ptr orig;
int count = 0; /* number of unmatched opposites found */
int check = 0;
char c;
orig = start = cpb( start );
while ((c = *--start) && (c != CONTROL_Z)) {
check++;
if (opp == c)
count++;
else if (target == c) {
if (count == 0)
break;
--count;
}
if (check > 8000) {
start = cpb( start );
check = 0;
}
}
if (c == CONTROL_Z)
start = orig;
return( start );
}
/*
* Name: match_pair
* Purpose: To find the corresponding pair to the character under the
* cursor.
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
* Notes: Searching is very simple-minded, and does not cope with things
* like brackets embedded within quoted strings.
*/
void match_pair( window )
windows *window;
{
text_ptr orig; /* cursor location in text */
int c;
/*
* make sure the character under the cursor is one that has a
* matched pair
*/
if (window->rcol >= linelen( window->cursor ))
return;
window->cursor = cpf( window->cursor );
orig = window->cursor + window->rcol;
c = *orig;
if (strchr( "[]{}()", c ) == NULL)
return;
/*
* find the matching pair
*/
switch (c) {
case '[':
orig = scan_forward( orig, '[', ']' );
break;
case '(':
orig = scan_forward( orig, '(', ')' );
break;
case '{':
orig = scan_forward( orig, '{', '}' );
break;
case ']':
orig = scan_backward( orig, ']', '[' );
break;
case ')':
orig = scan_backward( orig, ')', '(' );
break;
case '}':
orig = scan_backward( orig, '}', '{' );
break;
}
/*
* now show the user what we have found
*/
find_adjust( window, orig );
}
/*
* Name: goto_line
* Purpose: To move the cursor to a particular line in the file
* Date: June 5, 1991
* Passed: window: information allowing access to current window etc
*/
void goto_line( window )
windows *window;
{
long number; /* line number selected */
long i; /* line counter */
char num_str[MAX_COLS]; /* line number as string */
text_ptr p; /* used to scan through file counting lines */
int prompt_line;
prompt_line = window->bottom_line;
/*
* find out where we are going
*/
num_str[0] = '\0';
if (get_name( "Line number: ", prompt_line, num_str,
g_display.message_color ) != OK)
return;
number = atol( num_str );
if (number > 0 && number <= window->file_info->length) {
p = window->cursor;
i = window->rline;
if (number < window->rline) {
p = cpb( p );
for (; i>number; i--)
p = find_prev( p );
} else {
cpf( p );
for (; i<number; i++)
p = find_next( p );
}
find_adjust( window, p );
} else {
strcat( num_str, "must be in the range 1 - " );
ltoa( window->file_info->length, num_str+25, 10 );
error( WARNING, prompt_line, num_str );
}
}